# -*- coding: utf-8 -*-
# Author: xiantingDeng
# File: element.py
# Time: 17:37

import time

import allure
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait

from common.utils.file_util import get_img_file_path


class WebTools():
    def __init__(self, driver, log):
        self.__driver = driver
        self.__log = log
        self.action = ActionChains(self.__driver)

    def get_element(self, locator, ignore_error=False):
        '''
        以不等待方式获取页面元素
        :param locator: 定位方式以字典传输
        :param ignore_error: 是否忽略异常，默认不忽略
        :return: 查找到的元素
        '''
        select_by = locator.get("selector")
        value = locator.get("selector_by")
        try:
            if select_by == "id":
                return self.__driver.find_element_by_id(value)
            elif select_by == "name":
                return self.__driver.find_element_by_name(value)
            elif select_by == "link_text":
                return self.__driver.find_element_by_link_text(value)
            elif select_by == "className":
                return self.__driver.find_element_by_class_name(value)
            elif select_by == "xpath":
                return self.__driver.find_element_by_xpath(value)
            elif select_by == "css":
                return self.__driver.find_element_by_css_selector(value)
            else:
                raise NameError("Please enter a valid type of targeting elements.")
        except Exception as e:
            if not ignore_error:
                self.__log.error(e)
                raise e
            else:
                return False

    def get_element_wait(self, locator, timeout=5, ignore_error=False):
        '''
        以显示方法获取页面元素
        :param locator: 定位方式，以字典方式传递
        :param timeout: 超时时间
        :param ignore_error: 是否忽略异常
        :return: 查找到的元素
        '''
        select_by = locator.get("selector")
        value = locator.get("selector_by")
        try:
            if select_by == "id":
                return WebDriverWait(self.__driver, timeout).until(
                    EC.visibility_of_element_located((By.ID, value)))
            elif select_by == "name":
                return WebDriverWait(self.__driver, timeout).until(
                    EC.visibility_of_element_located((By.NAME, value)))
            elif select_by == "link_text":
                return WebDriverWait(self.__driver, timeout).until(
                    (EC.visibility_of_element_located((By.LINK_TEXT, value))))
            elif select_by == "classname":
                return WebDriverWait(self.__driver, timeout).until(
                    EC.visibility_of_element_located((By.CLASS_NAME, value)))
            elif select_by == "xpath":
                return WebDriverWait(self.__driver, timeout).until(
                    EC.visibility_of_element_located((By.XPATH, value)))
            elif select_by == "css":
                return WebDriverWait(self.__driver, timeout).until(
                    EC.visibility_of_element_located((By.CSS_SELECTOR, value)))
            else:
                raise NameError("Please enter a valid type of targeting elements.")
        except Exception as e:
            if not ignore_error:
                self.__log.error(e)
                raise e
            else:
                return False

    def get_elements_wait(self, locatrt, timeout=5, ignore_error=False):
        '''
        显示等待方式获取页面多个元素
        :param locatrt: 定位方式，以定位方式传递
        :param timeout: 超时时间
        :param ignore_error: 是否忽略异常
        :return: 返回查找到的元素
        '''
        selector_by = locatrt.get("selector")
        value = locatrt.get("selector_by")
        try:
            if selector_by == "id":
                return WebDriverWait(self.__driver, timeout).until(lambda x: x.find_element_by_id(value))
            elif selector_by == "name":
                return WebDriverWait(self.__driver, timeout).until(lambda x: x.find_element_by_name(value))
            elif selector_by == "link_text":
                return WebDriverWait(self.__driver, timeout).until(lambda x: x.find_element_by_link_text(value))
            elif selector_by == "className":
                return WebDriverWait(self.__driver, timeout).until(lambda x: x.find_element_by__class_name(value))
            elif selector_by == "xpath":
                return WebDriverWait(self.__driver, timeout).until(lambda x: x.find_element_by_xpath(value))
            elif selector_by == "css":
                return WebDriverWait(self.__driver, timeout).until(lambda x: x.find_element_by_css_selector(value))
            else:
                raise NameError("Please enter a valid type of targeting elements.")
        except Exception as e:
            if not ignore_error:
                self.__log.error(e)
                raise e
            else:
                return False

    def element_status(self, element):
        '''
        元素状态检测，是否存在
        :param element: 待检测元素
        '''
        try:
            assert element.is_displayed() == True
            return True
        except:
            return False

    def verify_if_the_selected(self, element):
        '''
        验证控件是否被选中
        :param element: 待操作元素
        :return: True or False
        '''
        try:
            assert element.is_selected() == True
            return True
        except:
            return False

    def Jumpwepage(self, url, time_wait=3):
        '''
        打开网页地址
        :param url: url地址
        :param time_wait: 等待时间
        '''
        with allure.step("打开网页：{}".format(url)):
            try:
                self.__driver.get(url)
                self.__driver.maximize_window()
                self.__driver.implicitly_wait(10)
                if isinstance(time_wait, int):
                    time.sleep(time_wait)
                self.__log.info("{}：打开该网页成功".format(url))
            except Exception as e:
                self.__log.error("网页打开异常：{}".format(e))
                raise

    def custom_wicket(self, long, wide):
        '''
        自定义浏览器窗口大小
        :param long: 长
        :param wide: 宽
        :return:
        '''
        return self.__driver.set_window_size(long, wide)

    def forwawrd(self):
        '''
        浏览器前进操作
        :return:
        '''
        with allure.step("浏览器向前一步"):
            try:
                self.__driver.forward()
                self.__log.info("浏览器向前一步")
            except Exception as e:
                self.__log.error("浏览器向前失败：{}".format(e))
                raise

    def back(self):
        '''
        浏览器后退操作
        :return:
        '''
        with allure.step("浏览器后退一步"):
            try:
                self.__driver.back()
                self.__log.info("浏览器后退一步")
            except Exception as e:
                self.__log.error("浏览器后退异常：{}".format(e))
                raise

    def quit_rowser(self):
        '''
        关闭并停止浏览器服务
        :return:
        '''
        with allure.step("关闭并停止浏览器"):
            try:
                self.__driver.quit()
                self.__log.info("关闭并停止浏览器")
            except Exception as e:
                self.__log.error("关闭浏览器异常：{}".format(e))
                raise

    def click_element(self, element, msg):
        '''
        点击元素
        :param element: 待点击元素
        :param msg: 元素描述
        :return:
        '''
        with allure.step("{}：点击完成"):
            try:
                if self.element_status(element):
                    element.click()
                    self.__log.info("{}:点击完成".format(msg))
                else:
                    self.__log.warning("当前界面没有找到-【{}】".format(msg))
            except Exception as e:
                self.__log.error("{}界面点击操作，出现异常：{}".format(msg, e))

    def input_vlaue(self, element, values, msg):
        '''
        输入明文内容
        :param element: 待输入元素
        :param values: 待输入内容
        :param msg: 元素描述
        :return:
        '''
        with allure.step("{}：输入完成{}".format(msg, values)):
            try:
                if self.element_status(element):
                    element.send_keys(values)
                    self.__log.info("{}:完成输入：{}".format(msg, values))
                else:
                    self.__log.warning("当前界面没有找到-【{}】".format(msg))
            except Exception as e:
                self.__log.error("{}界面输入操作，出现异常：{}".format(msg, e))

    def element_clear(self, element, msg):
        '''
        清除输入框内容
        :param element: 待操作元素
        :param msg: 元素描述
        :return:
        '''
        with allure.step("{}：清除完成".format(msg)):
            try:
                if self.element_status(element):
                    element.clear()
                    self.__log.info("{}：内容清除完成".format(msg))
                else:
                    self.__log.warning("当前界面没有找到-【{}】".format(msg))
            except Exception as e:
                self.__log.error("{}界面清除内容，出现异常：{}".format(msg, e))

    def get_element_text(self, element, msg):
        '''
        获取元素文本内容
        :param element: 待操作元素
        :param msg: 元素描述
        :return:
        '''
        text = None
        with allure.step("{}：获取到的文本内容为：{}".format(msg, text)):
            try:
                if self.element_status(element):
                    text = element.text
                    return text
                else:
                    self.__log.warning("当前界面没有找到-【{}】".format(msg))
            except Exception as e:
                self.__log.error("{}界面获取文本内容，出现异常：{}".format(msg, e))

    def driver_screenshot(self, name):
        '''
        操作步骤截图，该截图会增加到allure测试报告的log日志中
        :param name: 图片名称
        :return: 返回截图路径
        '''
        img = get_img_file_path(name)
        with allure.step("正在进行截图操作：{}.png，存放路径为：{}".format(name, img)):
            self.__driver.save_screenshot(img)
            self.__log.info("截取屏幕成功，保存路径为：{}".format(img))
            with open(img, "rb") as f:
                file = f.read()
                allure.attach(file, name, allure.attachment_type.PNG)
            return img

    def get_title_text(self):
        '''
        获取当前页面的title
        :return: 返回获取到的title信息
        '''
        title = None
        with allure.step("当前的title为：{}".format(title)):
            try:
                title = self.__driver.title
                self.__log.info("当前的title为：{}".format(title))
                return title
            except Exception as e:
                self.__log.error("获取当前title异常：{}".format(e))
                raise

    def get_element_size(self, element):
        '''
        获取页面元素大小
        :return: 返回大小
        '''
        return element.size

    def switch_iframe(self, element, msg):
        '''
        切换到ifame框中
        :param element: 待操作元素
        :param msg: 元素描述
        :return: TRUE or FALSE
        '''
        with allure.step("切换到：{}iframe框中"):
            try:
                self.__driver.switch_to.frame(element)
                self.__log.info("切换到{}iframe框中成功".format(msg))
            except Exception as e:
                self.__log.error("切换到{}iframe框异常:{}".format(msg, e))
                raise

    def switch_the_main_document(self):
        '''
        从ifame中切回主文档
        :return:
        '''
        with allure.step("从iframe中切回到主文档"):
            try:
                self.__driver.switch_to.default_content()
                self.__log.info("从iframe中切回到主文档")
            except Exception as e:
                self.__log.error("从ifame框切换到主文档失败：{}".format(e))
                raise

    def switch_2iframe(self):
        '''
        从frame2再切回frame1
        :return:
        '''
        with allure.step("切换到上一层的iframe框中"):
            try:
                self.__driver.switch_to.parent_frame()
                self.__log.info("切换到上一层的iframe框中")
            except Exception as e:
                self.__log.error("切换上一层iframe框异常：{}".format(e))
                raise

    def get_handles(self):
        '''
        获取当前所有窗口的句柄
        :return: 返回句柄
        '''
        handles = None
        with allure.step("当前窗口的句柄有：{}".format(handles)):
            try:
                handles = self.__driver.window_handles
                self.__log.info("当前窗口的句柄有：{}".format(handles))
                return handles
            except Exception as e:
                self.__log.error("获取当前窗口所有句柄异常：{}".format(e))
                raise

    def get_handle(self):
        '''
        获取当前窗口句柄
        :return: 返回句柄
        '''
        handle = None
        with allure.step("当前窗口句柄:{}".format(handle)):
            try:
                handle = self.__driver.current_window_handle
                self.__log.info("当前窗口的句柄有：{}".format(handle))
                return handle
            except Exception as e:
                self.__log.error("获取当前窗口句柄异常：{}".format(e))
                raise

    def switching_window(self, handles, msg):
        '''
        切换到新窗口
        :param handles: 窗口句柄
        :param msg: 窗口描述
        :return:
        '''
        with allure.step("切换到{}窗口中".format(msg)):
            try:
                self.__driver.switch_to.window(handles)
                self.__log.info("切换到{}窗口中".format(msg))
            except Exception as e:
                self.__log.error("切换到{}窗口异常，原因：{}".format(msg, e))
                raise

    def refresh(self):
        '''
        刷新当前页面
        :return:
        '''
        with allure.step("刷新当前页面"):
            try:
                self.__driver.refresh()
                self.__log.info("刷新当前页面成功")
            except Exception as e:
                self.__log.error("刷新页面异常：{}".format(e))
                raise

    def multiple_choice(self, element, msg):
        '''
        进行单选
        :param element: 需要进行单选的元素
        :param msg: 单选的描述信息
        :return:
        '''
        with allure.step("单击选择：{}".format(msg)):
            try:
                if self.element_status(element):
                    if self.verify_if_the_selected(element):
                        element.click()
                    else:
                        self.__log.warning("该按钮已被选中")
                else:
                    self.__log.warning("当前界面没有找到-【{}】".format(msg))
            except Exception as e:
                self.__log.error("{}单选操作异常：{}".format(msg, e))

    def more_choose(self, elements, dx_list, msg):
        '''
        进行多选
        :param elements: 需要进行多选的元素
        :param dx_list: 需要选择的参数value值
        :param msg: 多选操作描述信息
        :return:
        '''
        with allure.step("多选操作：{}".format(msg)):
            try:
                if self.element_status(elements):
                    if elements.get_attribute("value") in dx_list:
                        max = 0
                        for element in elements:
                            if self.verify_if_the_selected(element):
                                self.__log.info("该按钮已被选中，无需重复操作")
                            else:
                                element.click()
                                self.__log.info("{}:已被选中".format(dx_list[0]))
                                max += 1
                    else:
                        self.__log.warning("当前界面没有找到-【{}】".format(msg))
            except Exception as e:
                self.__log.error("{}多选按钮操作异常：{}".format(msg, e))

    '''下面都是ActionChains方法'''

    def act_element_click(self, element, msg):
        '''
        进行单击操作
        :param element: 待单击元素
        :param msg: 待单击元素描述
        :return:
        '''
        with allure.step("{}:执行单击操作".format(msg)):
            try:
                if self.element_status(element):
                    self.action.click(element)
                    self.action.perform()
                    self.__log.info("{}：单击完成".format(msg))
                else:
                    self.__log.warning("当前界面没有找到-【{}】".format(msg))
            except Exception as e:
                self.__log.error("{}单击操作出现异常：{}".format(msg,e))

    def act_click_and_hold(self, element, msg):
        '''
        点击鼠标左键不松开
        :param element: 待单击元素
        :param msg: 待单击元素描述
        :return:
        '''
        with allure.step("{}:执行单击操作".format(msg)):
            try:
                if self.element_status(element):
                    self.action.click_and_hold(element)
                    self.action.perform()
                    self.__log.info("{}：单击完成".format(msg))
                else:
                    self.__log.warning("当前界面没有找到-【{}】".format(msg))
            except Exception as e:
                self.__log.error("{}单击不松开操作出现异常：{}".format(msg,e))

    def act_context_click(self, element, msg):
        '''
        点击鼠标右键
        :param element: 待右击元素
        :param msg: 待右击元素描述
        :return:
        '''
        with allure.step("{}:执行右击操作".format(msg)):
            try:
                if self.element_status(element):
                    self.action.context_click(element)
                    self.action.perform()
                    self.__log.info("{}:右击操作完成".format(msg))
                else:
                    self.__log.warning("当前界面没有找到-【{}】".format(msg))
            except Exception as e:
                self.__log.error("{}右击操作出现异常：{}".format(msg,e))

    def act_double_click(self, element, msg):
        '''
        双击操作
        :param element: 待双击元素
        :param msg: 待双击元素描述
        :return:
        '''
        with allure.step("{}：执行双击操作".format(msg)):
            try:
                if self.element_status(element):
                    self.action.double_click(element)
                    self.action.perform()
                    self.__log.info("{}:双击操作完成".format(msg))
                else:
                    self.__log.warning("当前界面没有找到-【{}】".format(msg))
            except Exception as e:
                self.__log.error("{}双击操作出现异常：{}".format(msg,e))

    def act_drag_and_drop(self, source, target, msg):
        '''
        拖拽到某个元素然后松开
        :param source: 待拖动元素
        :param target: 放开元素
        :param msg: 待操作信息描述
        :return:
        '''
        with allure.step("{}:开始执行拖动操作".format(msg)):
            try:
                if self.element_status(source) and self.element_status(target):
                    self.action.drag_and_drop(source, target)
                    self.action.perform()
                    self.__log.info("{}：拖动操作完成".format(msg))
                else:
                    self.__log.warning("当前界面没有找到-【{}】".format(msg))
            except Exception as e:
                self.__log.error("{}拖动操作异常：{}".format(msg,e))

    def act_drag_and_drop_by_offset(self, element, msg, x=0, y=0):
        '''
        拖拽到某个坐标然后松开
        :param element: 待拖动元素
        :param x: x值 默认等于0
        :param y: y值 默认等于0
        :param msg: 拖动位置描述
        :return:
        '''
        with allure.step("{}：拖动到坐标{}-{}".format(msg, x, y)):
            try:
                if self.element_status(element):
                    self.action.drag_and_drop_by_offset(element, x, y)
                    self.action.perform()
                    self.__log.info("{}：拖动到{}-{}操作完成".format(msg, x, y))
                else:
                    self.__log.warning("当前界面没有找到-【{}】".format(msg))
            except Exception as e:
                self.__log.error("{}执行拖拽操作异常：{}".format(msg,e))

    # 待使用在优化
    def act_key_down(self, element, value, msg):
        '''
        组合键
        :param element: 待操作元素
        :param value: 待按下去的按钮值
        :param msg: 操作描述
        :return:
        '''
        with allure.step("{}：按键操作".format(msg)):
            try:
                if self.element_status(element):
                    self.action.key_down(Keys.CONTROL).send_keys(value).key_up(Keys.CONTROL).perform()
            except:
                pass

    def act_move_by_offset(self, x, y, msg):
        '''
        鼠标从当前位置移动到某个坐标
        :param x: x坐标轴
        :param y: y坐标轴
        :param msg: 操作描述
        :return:
        '''
        with allure.step("{}：移动坐标轴到{}-{}".format(msg, x, y)):
            try:
                self.action.move_by_offset(x, y)
                self.__log.info("{}：移动坐标轴到{}-{}成功".format(msg, x, y))
            except Exception as e:
                self.__log.error("{}移动坐标轴异常：{}".format(msg,e))

    def act_move_to_element(self, element, msg):
        '''
        鼠标移动到某个元素
        :param element: 待移动元素
        :param msg: 操作描述
        :return:
        '''
        with allure.step("将鼠标移动至：{}".format(msg)):
            try:
                if self.element_status(element):
                    self.action.move_to_element(element)
                    self.__log.info("将鼠标移动至：{}成功".format(msg))
                else:
                    self.__log.warning("当前界面没有找到-【{}】".format(msg))
            except Exception as e:
                self.__log.error("{}移动鼠标到元素操作异常：{}".format(msg,e))

    def act_move_to_element_with_offset(self, element, x, y, msg):
        '''
        移动到距某个元素（左上角坐标）多少距离的位置
        :param element: 待移动元素
        :param x: x坐标轴
        :param y: y坐标轴
        :param msg: 移动操作描述
        :return:
        '''
        with allure.step("{}:移动到距离该位置的{}-{}".format(msg, x, y)):
            try:
                if self.element_status(element):
                    self.action.move_to_element_with_offset(element, x, y)
                    self.__log.info("{}:移动到距离该位置的{}-{}完成".format(msg, x, y))
                else:
                    self.__log.warning("当前界面没有找到-【{}】".format(msg))
            except Exception as e:
                self.__log.error("{}移动鼠标位置报错:{}".format(msg,e))

